<!-- 基础新增或编辑表单 -->
<template>
    <el-form :model="form" :label-position="labelPosition" :label-width="labelWidth" class="base-form" ref="formRef"
        :validate-on-rule-change="false" :rules="rules" :inline="inline">
        <el-row>
            <template v-for="field, index in fields">
                <el-col :span="field.col || 24">
                    <el-form-item :label="field.label" :required="field.required" v-if="field.type != 'operations' && showField(field)"
                        :prop="field.prop">
                        <BaseFormField :field="field" :form="form" :fields="fields" :readonly="readonly" />
                    </el-form-item>
                </el-col>
            </template>

            <slot :form="form">
            </slot>
        </el-row>
    </el-form>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import * as _ from 'lodash';
import BaseFormField from './field.vue';

const form = defineModel();
const rules = ref({});
const formRef = ref();

const props = defineProps({
    // 字段列表
    fields: Array,
    labelWidth: {
        type: String,
        default: '100px'
    },
    labelPosition: {
        type: String,
        default: 'right'
    },
    readonly: {
        type: Boolean,
        default: false
    },
    inline: {
        type: Boolean,
        default: false
    }
})

onMounted(() => {
    // 加载校验规则
    loadRules()
    if (_.isEmpty(form.value)) {
        form.value = getFormDefaultModel(props.fields)
    }
})


// 获取默认值
function getFormDefaultModel(fields) {
    let obj = {}
    fields.forEach(field => {
        if (field.default || field.default == 0) {
            obj[field.prop] = field.default
        } else if (field.type == 'table') {
            obj[field.prop] = []
        }
    })

    return obj;
}

function validate(func) {
    return formRef.value.validate(func)
}

// 加载校验规则
function loadRules() {
    if (!props.fields) {
        return;
    }

    props.fields.forEach(field => {
        let fieldRules = []
        if (field.required) {
            fieldRules.push({ required: true, message: field.label + '不能为空' })
        }

        const validation = field.validation
        if (validation?.validator) {
            const validator = validation.validator;
            const type = validation.type;
            const msg = validation.msg;

            // 可能是校验函数或者正则表达式或者内置校验
            if (type == 'func' || _.isFunction(validator)) {
                fieldRules.push({
                    trigger: 'blur',
                    validator: (rule, val, callback) => validator(rule, val, callback, form.value)
                })
            } else if (type == 'regex') {
                let rule = {
                    validator: (rule, val, callback) => {
                        console.log(validator)
                        if (!val) {
                            callback()
                            return
                        }

                        if (!val.match(validator)) {
                            callback(new Error(msg || '校验失败'))
                            return;
                        }

                        callback();
                    }
                }

                // 正则校验
                fieldRules.push(rule)
            }
        }

        if (field.maxLength) {
            fieldRules.push({
                max: field.maxLength,
                message: field.label + '长度不能超过' + field.maxLength
            })
        }

        if (field.max || field.max == 0) {
            fieldRules.push({
                max: field.max,
                message: field.label + '不能超过' + field.max
            })
        }


        rules.value[field.prop] = fieldRules;
    })
}

function showField(field) {
    if (field.display == false) {
        return false
    }

    if (!field.display) {
        return true
    }

    if (_.isFunction(field.display)) {
        return field.display(form.value)
    }

    return field.display
}

defineExpose({
    validate
})
</script>

<style lang="scss" scoped>
.base-form {
    :deep() {
        label {
            align-items: flex-start;
        }
    }
}
</style>